////////////////////////////////////////////////////////////////////////////////
// Copyright 2010 Michael Schmalle - Teoti Graphix, LLC
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
// http://www.apache.org/licenses/LICENSE-2.0 
// 
// Unless required by applicable law or agreed to in writing, software 
// distributed under the License is distributed on an "AS IS" BASIS, 
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and 
// limitations under the License
// 
// Author: Michael Schmalle, Principal Architect
// mschmalle at teotigraphix dot com
////////////////////////////////////////////////////////////////////////////////

package org.teotigraphix.swizplugin.registry.core
{

import flash.events.EventDispatcher;
import flash.utils.getDefinitionByName;

import mx.collections.ArrayCollection;
import mx.core.FlexGlobals;

import org.swizframework.core.IBeanFactory;
import org.swizframework.core.IDisposable;
import org.swizframework.core.ISwiz;
import org.swizframework.core.ISwizAware;
import org.swizframework.utils.logging.SwizLogger;
import org.teotigraphix.swizplugin.api.IExecutableRequest;
import org.teotigraphix.swizplugin.api.IPlugin;
import org.teotigraphix.swizplugin.bundles.IBundle;
import org.teotigraphix.swizplugin.core.ExecutableRequest;
import org.teotigraphix.swizplugin.core.PluginExtension;
import org.teotigraphix.swizplugin.core.PluginExtensionPoint;
import org.teotigraphix.swizplugin.core.XMLPluginExtension;
import org.teotigraphix.swizplugin.events.BundleServiceEvent;
import org.teotigraphix.swizplugin.events.ExtensionRegistryChangeEvent;
import org.teotigraphix.swizplugin.events.ExtensionRegistryChangeKind;
import org.teotigraphix.swizplugin.events.ExtensionRegistryChangeType;
import org.teotigraphix.swizplugin.events.ExtensionRegistryEvent;
import org.teotigraphix.swizplugin.registry.IContributor;
import org.teotigraphix.swizplugin.registry.IExtension;
import org.teotigraphix.swizplugin.registry.IExtensionDescriptor;
import org.teotigraphix.swizplugin.registry.IExtensionPoint;
import org.teotigraphix.swizplugin.registry.IExtensionRegistry;
import org.teotigraphix.swizplugin.services.BundleService;
import org.teotigraphix.swizplugin.services.IBeanService;
import org.teotigraphix.swizplugin.services.IBundleService;

/**
 * The <strong>ExtensionRegistry</strong> is the default implementation 
 * of <code>IExtensionRegistry</code>.
 */
public class ExtensionRegistry extends EventDispatcher 
	implements IExtensionRegistry, IDisposable, ISwizAware
{
	//--------------------------------------------------------------------------
	//
	//  Public :: Variables
	//
	//--------------------------------------------------------------------------
	
	[Inject]
	public var beanService:IBeanService;
	
	[Inject]
	public var bundleService:IBundleService;
	
	[PostConstruct]
	public function postConstruct():void
	{
		bundleService.addEventListener(
			BundleServiceEvent.BUNDLE_SERVICE_STARTED, bundleServiceStarted);
		
		bundleService.addEventListener(
			BundleServiceEvent.BUNDLE_ADD, bundleServiceAdd);
	}
	
	protected function bundleServiceStarted(event:BundleServiceEvent):void
	{
		var bundles:Array = bundleService.getBundles();
		
		var len:int = bundles.length;
		for (var i:int = 0; i < len; i++)
		{
			internalAddBundle(IBundle(bundles[i]));
		}
		
		dispatchEvent(new ExtensionRegistryEvent(
			ExtensionRegistryEvent.REGISTRY_STARTED));
	}
	
	protected function bundleServiceAdd(event:BundleServiceEvent):void
	{
		// only add plugin after the bundle service has done it's
		// initial activation of plugins
		if (!bundleService.active)
			return;
		
		internalAddBundle(event.bundle);
	}
	
	protected function internalAddBundle(bundle:IBundle):void
	{
		addBundle(bundle);
	}
	
	//--------------------------------------------------------------------------
	//
	//  Protected :: Variables
	//
	//--------------------------------------------------------------------------
	
	/**
	 * @private
	 */
	protected var logger:SwizLogger = SwizLogger.getLogger(this);
	
	//--------------------------------------------------------------------------
	//
	//  ISwizAware API :: Properties
	//
	//--------------------------------------------------------------------------
	
	//----------------------------------
	//  swiz
	//----------------------------------
	
	/**
	 * @private
	 */
	private var _swiz:ISwiz;
	
	/**
	 * @copy org.swizframework.core.ISwizAware#swiz
	 */
	public function get swiz():ISwiz
	{
		return _swiz;
	}
	
	/**
	 * @private
	 */	
	public function set swiz(value:ISwiz):void
	{
		_swiz = value;
	}
	
	//--------------------------------------------------------------------------
	//
	//  Public Static :: Properties
	//
	//--------------------------------------------------------------------------
	
	/**
	 * @private
	 */
	private var _nextID:int = -1;
	
	/**
	 * @private
	 */
	internal function nextID():int
	{
		return _nextID++;
	}
	
	//--------------------------------------------------------------------------
	//
	//  Private :: Variables
	//
	//--------------------------------------------------------------------------
	
	/**
	 * @private
	 * list of IBundleContributor
	 */
	private var _contributors:ArrayCollection;
	
	/**
	 * @private
	 * list of IExtensionPoint
	 */
	private var _extensionPoints:ArrayCollection;
	
	/**
	 * @private
	 * list of IExtension, IExtensionPoint, IExtensionDescriptor
	 */
	private var _registryClients:ArrayCollection;
	
	//--------------------------------------------------------------------------
	//
	//  Constructor
	//
	//--------------------------------------------------------------------------
	
	/**
	 * Constructor.
	 * @private
	 */
	public function ExtensionRegistry()
	{
		super();
		
		_nextID = 0;
		
		_contributors = new ArrayCollection();
		_extensionPoints = new ArrayCollection();
		_registryClients = new ArrayCollection();
	}
	
	//--------------------------------------------------------------------------
	//
	//  IDisposable API :: Methods
	//
	//--------------------------------------------------------------------------
	
	/**
	 * Destroys the extension regitry and removes all instances.
	 */
	public function destroy():void
	{
		_nextID = 0;
		
		_contributors = new ArrayCollection();
		_extensionPoints = new ArrayCollection();
		_registryClients = new ArrayCollection();
	}
	
	//--------------------------------------------------------------------------
	//
	//  IExtensionRegistry API :: Methods
	//
	//--------------------------------------------------------------------------
	
	/**
	 * @copy org.teotigraphix.swizplugin.registry.IExtensionRegistry#createExecutableExtension()
	 */
	public function createExecutableExtension(contributor:IContributor,
											  className:String):Object
	{
		var bundle:IBundle = bundleService.getBundle(contributor.id);
		if (!bundle)
		{
			logger.warn("IBundle {0} was not found", contributor.name);
			return null;
		}
		
		var token:IExecutableRequest = bundle.loadClass(className);
		if  (token == null)
		{
			logger.error("IExtensionRegistry.createExecutableExtension()" +
				" could not create '{0}' class in '{1}' contributor", 
				className, contributor.name);
		}
		
		return token;
	}
	
	/**
	 * @copy org.teotigraphix.swizplugin.registry.IExtensionRegistry#addContributor()
	 */
	public function addContributor(client:IContributor):void
	{
		var registryContributor:RegistryContributor = client as RegistryContributor;
		
		if (!containsContributor(registryContributor))
		{
			_contributors.addItem(client);
		}
	}
	
	/**
	 * @copy org.teotigraphix.swizplugin.registry.IExtensionRegistry#addExtensionPoint()
	 */
	public function addExtensionPoint(contributor:IContributor,
									  extensionPointID:String,
									  extensionPointName:String):IExtensionPoint
	{
		return internalAddExtensionPoint(contributor, extensionPointID, extensionPointName);
	}
	
	/**
	 * @copy org.teotigraphix.swizplugin.registry.IExtensionRegistry#addExtensionDescriptor()
	 */
	public function addExtensionDescriptor(contributor:IContributor,
										   parentID:int,
										   name:String,
										   properties:Object):IExtensionDescriptor
	{
		return internalAddExtensionDescriptor(contributor, parentID, name, properties);
	}
	
	/**
	 * @copy org.teotigraphix.swizplugin.registry.IExtensionRegistry#addExtension()
	 */
	public function addExtension(contributor:IContributor,
								 extensionID:String,
								 extensionPointID:String,
								 extensionName:String):IExtension 
	{
		if (extensionID == null)
			extensionID = "";
		
		return internalAddExtension(
			contributor, extensionID, extensionPointID, extensionName);
	}
	
	/**
	 * @copy org.teotigraphix.swizplugin.registry.IExtensionRegistry#getContributor()
	 */
	public function getContributor(id:int):IContributor
	{
		for (var i:int = 0; i < _contributors.length; i++)
		{
			var element:IContributor = _contributors[i] as IContributor;
			if (element.id == id)
				return element;
		}
		
		return null;
	}	
	
	/**
	 * @copy org.teotigraphix.swizplugin.registry.IExtensionRegistry#getExtensionPoint()
	 */
	public function getExtensionPoint(nameSpace:String, 
									  pointName:String = null):IExtensionPoint
	{
		if (nameSpace == null)
			return null;
		
		if (pointName == null)
			return getInternalExtensionPoint(nameSpace);
		
		return getInternalExtensionPoint(nameSpace + "." + pointName);
	}
	
	/**
	 * @copy org.teotigraphix.swizplugin.registry.IExtensionRegistry#getExtensionPoints()
	 */
	public function getExtensionPoints():Array
	{
		var result:Array = [];
		
		for (var i:int = 0; i < _extensionPoints.length; i++)
		{
			result.push(_extensionPoints[i]);
		}
		
		return result;
	}
	
	/**
	 * @copy org.teotigraphix.swizplugin.registry.IExtensionRegistry#getExtension()
	 */
	public function getExtension(extensionID:String,
								 extensionPointID:String = null):IExtension
	{
		// if no epoint is specified, chop it off the extension's id
		if (extensionPointID == null)
		{
			// if the extension is simple, there is no way to resolve 
			// this extension correctly
			if (extensionID.indexOf(".") == -1)
				return null;
			
			extensionPointID = extensionID.substring(0, 
				extensionID.lastIndexOf("."));
			
			extensionID = extensionID.substring(extensionID.lastIndexOf(".") + 1);
		}
		
		// get all extension for the point
		var point:IExtensionPoint = getExtensionPoint(extensionPointID);
		if (point == null)
			return null;
		
		return point.getExtension(extensionID);
	}
	
	/**
	 * @copy org.teotigraphix.swizplugin.registry.IExtensionRegistry#getExtensionFor()
	 */
	public function getExtensionFor(pluginID:String,
									extensionPointName:String,
									extensionID:String):IExtension
	{
		var point:IExtensionPoint = getExtensionPoint(pluginID + "." + extensionPointName);
		if (point == null)
			return null;
		
		return point.getExtension(extensionID);
	}
	
	/**
	 * @copy org.teotigraphix.swizplugin.registry.IExtensionRegistry#getExtensionsForContributor()
	 */
	public function getExtensionsForContributor(contributor:IContributor,
												extensionPointName:String = null):Array
	{
		var result:Array = [];
		
		var len:int = _registryClients.length;
		for (var i:int = 0; i < len; i++)
		{
			var element:IExtension = _registryClients[i] as IExtension;
			if (element && element.contributor === contributor)
			{
				if (extensionPointName == null 
					|| element.extensionPointID == extensionPointName)
				{
					result.push(element);
				}
			}
		}
		
		return result;
	}
	
	/**
	 * @copy org.teotigraphix.swizplugin.registry.IExtensionRegistry#getExtensions()
	 */
	public function getExtensions(extensionPointID:String):Array
	{
		var result:Array = [];
		
		for (var i:int = 0; i < _extensionPoints.length; i++)
		{
			var point:RegistryExtensionPoint = _extensionPoints[i] as RegistryExtensionPoint;
			if (point.uniqueID == extensionPointID) 
			{
				for (var j:int = 0; j < point.children.length; j++) 
				{
					result.push(getRegistryExtension(point.children[j]));
				}
			}
		}
		
		return result;
	}
	
	/**
	 * @copy org.teotigraphix.swizplugin.registry.IExtensionRegistry#getExtensionDescriptors()
	 */
	public function getExtensionDescriptors(extension:IExtension,
											extensionPointID:String = null):Array
	{
		var result:Array = [];
		var children:Array = RegistryClient(extension).children;
		
		for (var i:int = 0; i < children.length; i++)
		{
			var descriptor:IExtensionDescriptor = 
				_registryClients.getItemAt(children[i]) as IExtensionDescriptor;
			
			if (extensionPointID == null 
				|| descriptor.extension.extensionPointID == extensionPointID)
				result.push(descriptor);
		}
		
		return result;
	}
	
	/**
	 * @copy org.teotigraphix.swizplugin.registry.IExtensionRegistry#getExtensionDescriptorsFor()
	 */
	public function getExtensionDescriptorsFor(extensionPointID:String):Array
	{
		var result:Array = [];
		
		var extension:Array = getExtensions(extensionPointID);
		
		for each (var ext:RegistryExtension in extension) 
		{
			var items:Array = ext.getExtensionDescriptors();
			if (items is Array)
			{
				result = result.concat(items);
			}
		}
		
		return result;
	}
	
	/**
	 * @copy org.teotigraphix.swizplugin.registry.IExtensionRegistry#getExtensionDescriptorsByID()
	 */
	public function getExtensionDescriptorByID(descriptorID:String, 
											   extensionPointID:String):IExtensionDescriptor
	{
		var descriptors:Array = getExtensionDescriptorsFor(extensionPointID);
		if (!descriptors)
			return null;
		
		for (var i:int = 0; i < descriptors.length; i++)
		{
			var element:IExtensionDescriptor = descriptors[i] as IExtensionDescriptor;
			if (element.uid == descriptorID)
				return element;
		}
		
		return null;
	}
	
	//--------------------------------------------------------------------------
	//
	//  Protected :: Methods
	//
	//--------------------------------------------------------------------------
	
	/**
	 * @private
	 */
	protected function internalAddExtensionPoint(contributor:IContributor,
												 identifier:String, 
												 displayName:String):IExtensionPoint
	{
		var internalContributor:RegistryContributor = null;
		var contributorID:int = -1;
		
		// contribution from a class service
		if (contributor == null)
		{
			contributor = createContributor(-1, "ep://" + identifier);
			addContributor(contributor);
		}
		
		internalContributor = RegistryContributor(contributor);
		contributorID = internalContributor.id;
		
		var point:RegistryExtensionPoint = createExtensionPoint(nextID()) as RegistryExtensionPoint;
		
		var uniqueID:String;		
		var namespaceID:String;
		
		var start:int = identifier.lastIndexOf(".");
		if (start == -1) 
		{
			namespaceID = internalContributor.name;
			uniqueID = namespaceID + "." + identifier;
		} 
		else
		{
			namespaceID = identifier.substring(0, start);
			uniqueID = identifier;
		}
		
		point.uniqueID = uniqueID;
		point.namespaceID = namespaceID;
		
		point.label = displayName;
		
		point.contributorID = contributorID;
		
		_extensionPoints.addItem(point);
		
		addRegistryClient(point);
		
		// find all extensions that implement this point.
		for (var i:int = 0; i < _registryClients.length; i++)
		{
			var client:RegistryClient = _registryClients[i] as RegistryClient;
			if (client is RegistryExtension)
			{
				var extension:RegistryExtension = client as RegistryExtension;
				if (extension.extensionPointID == point.uniqueID)
				{
					// add the client to the points's children
					if (point.children == null)
					{
						point.children = [];
					}
					
					point.children.push(client.registryID);
					break;
				}
			}
		}
		
		logger.debug("addExtensionPoint( {0} )", point.uniqueID);
		
		dispatchRegistryChangeEvent(
			ExtensionRegistryChangeKind.ADD,
			ExtensionRegistryChangeType.EXTENSION_POINT,
			point);
		
		return point;
	}
	
	/**
	 * @private
	 * eid org.example.plugins.myTraceOneExtension
	 * epid org.example.points.trace
	 * en null
	 */
	protected function internalAddExtension(contributor:IContributor,
											extensionID:String,
											extensionPointID:String, 
											displayName:String):IExtension
	{
		var internalContributor:RegistryContributor = RegistryContributor(contributor);
		var contributorID:int = internalContributor.id;
		
		var extension:RegistryExtension = createExtension(nextID()) as RegistryExtension;
		
		var simpleIdentifier:String;
		var namespaceName:String;
		
		var simpleIdStart:int = extensionID.lastIndexOf( '.' );
		if (simpleIdStart != -1) 
		{
			simpleIdentifier = extensionID.substring( simpleIdStart + 1 );
			namespaceName = extensionID.substring( 0, simpleIdStart );
		} 
		else 
		{
			// if there is no package must be reletive to IConfigurationElement
			simpleIdentifier = extensionID;
			namespaceName = internalContributor.name;
		}
		
		extension.contributorID = contributorID;
		extension.extensionPointID = extensionPointID;
		extension.simpleID = simpleIdentifier;
		extension.namespaceID = namespaceName;
		extension.label = displayName;
		
		var point:RegistryExtensionPoint = getExtensionPoint(
			extensionPointID) as RegistryExtensionPoint;
		
		if (point)
		{
			point.children.push(extension.registryID);
		}
		
		addRegistryClient(extension);
		
		var pointName:String = "";
		if (point)
			pointName = "[ " + point.simpleID + " ]";
		
		if (displayName)
			logger.debug("addExtension( {0} )", displayName);
		else if (!simpleIdentifier)
			logger.debug("addExtension( {0}{1} )", namespaceName, pointName);
		else
			logger.debug("addExtension( {0}.{1} )", simpleIdentifier, namespaceName);
		
		dispatchRegistryChangeEvent(
			ExtensionRegistryChangeKind.ADD,
			ExtensionRegistryChangeType.EXTENSION,
			extension);
		
		return extension;
	}
	
	/**
	 * @private
	 */
	protected function internalAddExtensionDescriptor(contributor:IContributor,
													  parentID:int,
													  name:String,
													  properties:Object):IExtensionDescriptor
	{
		var descriptor:RegistryExtensionDescriptor = 
			createExtensionDescriptor(nextID()) as RegistryExtensionDescriptor;
		
		descriptor.contributorID = contributor.id;
		descriptor.parentID = parentID;
		descriptor.name = name;
		descriptor.properties = properties;
		
		addRegistryClient(descriptor);
		
		// the descriptor is specail, it should not be orphaned since we
		// are passing a parentID [IExtension or IExtensionDescriptor]
		
		var duplicate:Boolean = false;
		var parent:RegistryClient = getRegistryClient(parentID);
		if (parent != null)
		{
			if (parent.children == null)
			{
				parent.children = [descriptor.registryID];
			}
			else
			{
				// check for duplicates
				for (var i:int = 0; i < parent.children.length; i++)
				{
					var index:int = parent.children[i] as int;
					if (index == descriptor.registryID)
					{
						duplicate = true;
						break;
					}
				}
			}
		}
		
		if (!duplicate)
		{
			parent.children.push(descriptor.registryID);
		}
		else
		{
			// Log.error("Duplicated descriptor on parentID")
			logger.error("Duplicated descriptor on parentID( {0} )", parentID);
		}
		
		logger.debug("addExtensionDescriptor( {0} )", name);
		
		dispatchRegistryChangeEvent(
			ExtensionRegistryChangeKind.ADD,
			ExtensionRegistryChangeType.EXTENSION_DESCRIPTOR,
			descriptor);
		
		return descriptor;
	}
	
	//--------------------------------------------------------------------------
	//
	//  Internal :: Methods
	//
	//--------------------------------------------------------------------------
	
	internal function containsContributor(client:IContributor):Boolean
	{
		for (var i:int = 0; i < _contributors.length; i++)
		{
			var element:IContributor = _contributors[i] as IContributor;
			if (element == client)
				return true;
		}
		
		return false;
	}
	
	/**
	 * @private
	 */
	internal function addRegistryClient(client:RegistryClient):void
	{
		if (!containsID(client.registryID)) 
		{
			_registryClients.addItem(client);
		}
	}
	
	private function containsID(registryID:int):Boolean
	{
		for (var i:int = 0; i < _registryClients.length; i++)
		{
			var client:RegistryClient = _registryClients[i] as RegistryClient;
			if (client.registryID == registryID)
				return true;
		}
		
		return false;
	}
	
	/**
	 * @private
	 */
	internal function getRegistryClient(registryID:int):RegistryClient
	{
		return _registryClients.getItemAt(registryID) as RegistryClient;
	}
	
	/**
	 * @private
	 */
	internal function getRegistryExtension(registryID:int):IExtension
	{
		var client:RegistryExtension = _registryClients.
			getItemAt(registryID) as RegistryExtension;
		
		if (client != null)
			return client;
		
		return null;
	}
	
	/**
	 * @private
	 */
	internal function getRegistryExtensionDescriptor(registryID:int):IExtensionDescriptor
	{
		var client:RegistryExtensionDescriptor = _registryClients.
			getItemAt(registryID) as RegistryExtensionDescriptor;
		
		if (client != null)
			return client;
		
		return null;
	}
	
	/**
	 * @private
	 */
	internal function createExtensionPoint(registryID:int):IExtensionPoint
	{
		return new RegistryExtensionPoint(this, registryID, []);
	}
	
	/**
	 * @private
	 */
	internal function createExtension(registryID:int):IExtension
	{
		return new RegistryExtension(this, registryID, []);
	}
	
	/**
	 * @private
	 */
	internal function createExtensionDescriptor(registryID:int):IExtensionDescriptor
	{
		return new RegistryExtensionDescriptor(this, registryID);
	}
	
	/**
	 * @private
	 */	
	internal function createContributor(bundleID:int, name:String):IContributor
	{
		return new RegistryContributor(bundleID, name);
	}
	
	
	private function getInternalExtensionPoint(extensionPointID:String):IExtensionPoint
	{
		for (var i:int = 0; i < _extensionPoints.length; i++)
		{
			var element:IExtensionPoint = _extensionPoints[i] as IExtensionPoint;
			if (element.uniqueID == extensionPointID)
				return element;
		}
		
		return null;
	}
	
	public function addBundle(bundle:IBundle):void
	{
		logger.debug("<<<< IExtensionRegistry.addPlugin( {0} )", bundle.symbolicName);
		
		// create an IContributor
		var contributor:IContributor = createContributor(
			bundle.bundleID, bundle.symbolicName);
		
		// add the IContributor
		addContributor(contributor);
		
		var plugin:IPlugin = bundle.context.activator as IPlugin;
		if (!plugin)
		{
			logger.debug("No IPlugin found for '{0}' bundle", bundle.symbolicName);
			return;
		}
		
		// foreach extension point, parse and add to registry
		parseExtensionPoints(plugin, contributor);
		
		/// foreach extension, parse and add to registry
		parseExtensions(plugin, contributor);
		
		logger.debug(">>>> IExtensionRegistry.addPlugin( {0} )", plugin.uid);
	}
	
	//--------------------------------------------------------------------------
	//
	//  Private :: Methods
	//
	//--------------------------------------------------------------------------
	
	/**
	 * @private
	 */
	private function parseExtensionPoints(plugin:IPlugin, 
										  contributor:IContributor):void
	{
		if (plugin.points == null || plugin.points.length == 0)
			return;
		
		for (var i:int = 0; i < plugin.points.length; i++)
		{
			var point:PluginExtensionPoint = plugin.points[i] as PluginExtensionPoint;
			if (point.uid == null || point.uid == "")
			{
				// no error here just log the error and don't add the point
				continue;
			}
			
			addExtensionPoint(contributor, point.uid, point.name);
		}
	}
	
	/**
	 * @private
	 */
	private function parseExtensions(plugin:IPlugin, 
									 contributor:IContributor):void
	{
		if (plugin.extentions == null || plugin.extentions.length == 0)
			return;
		
		for (var i:int = 0; i < plugin.extentions.length; i++)
		{
			if (plugin.extentions[i] is PluginExtension)
			{
				parseExtension(PluginExtension(plugin.extentions[i]), contributor);
			}
			else if (plugin.extentions[i] is XMLPluginExtension)
			{
				parseXMLExtension(XMLPluginExtension(plugin.extentions[i]), contributor);
			}
		}
	}
	
	/**
	 * @private
	 */
	private function parseExtension(extension:PluginExtension, 
									contributor:IContributor):void
	{
		var extensionID:String = extension.uid;
		var extensionPointID:String = extension.point;
		var extensionLabel:String = extension.name;
		
		var registryExtension:RegistryExtension = addExtension(
			contributor, extensionID, extensionPointID, extensionLabel) as RegistryExtension;
		
		if (extension.children != null && extension.children.length > 0)
		{
			registryExtension.children = parseExensionDescriptors(
				extension.children, registryExtension);
		}
	}
	
	/**
	 * @private
	 */
	private function parseXMLExtension(extension:XMLPluginExtension, 
									   contributor:IContributor):void
	{
		var extensionID:String = extension.uid;
		var extensionPointID:String = extension.point;
		var extensionLabel:String = extension.name;
		
		var registryExtension:RegistryExtension = addExtension(
			contributor, extensionID, extensionPointID, extensionLabel) as RegistryExtension;
		
		if (extension.children != null && extension.children.length() > 0)
		{
			registryExtension.children = parseXMLExensionDescriptors(
				extension.children, registryExtension);
		}
	}
	
	/**
	 * @private
	 */
	private function parseExensionDescriptors(children:Array, 
											  extension:RegistryExtension):Array
	{
		return ExtensionParser.parseExensionDescriptors(
			this, children, extension.contributor, extension.registryID);	
	}
	
	/**
	 * @private
	 */
	private function parseXMLExensionDescriptors(children:XMLList, 
												 extension:RegistryExtension):Array
	{
		return ExtensionParser.parseXMLExensionDescriptors(
			this, children, extension.contributor, extension.registryID);	
	}
	
	
	/**
	 * @private
	 * Dispatch asyncronously so the method returns before listeners
	 * are updated
	 */
	private function dispatchRegistryChangeEvent(kind:String,
												 typeKind:int,
												 element:Object):void
	{
		if (hasEventListener(ExtensionRegistryChangeEvent.REGISTRY_CHANGE))
		{
			var e:ExtensionRegistryChangeEvent = 
				new ExtensionRegistryChangeEvent(kind, typeKind, element);
			
			//FlexGlobals.topLevelApplication.callLater(doDispatch, [e]);
			dispatchEvent(e);
		}
	}
	
	/**
	 * @private
	 */
	private function doDispatch(event:ExtensionRegistryEvent):void
	{
		dispatchEvent(event);
	}
}
}